import { Vec3, v3 } from 'cc';

export class CatmullRomSpline {
    /**
     * 生成Catmull-Rom样条曲线（自动处理控制点不足的情况）
     * @param controlPoints 控制点数组
     * @param segmentsPerSegment 每段之间的插值点数
     * @param closedLoop 是否闭合曲线
     * @returns 生成的曲线点数组
     */
    public static generateCurve(
        controlPoints: Vec3[],
        segmentsPerSegment: number = 20,
        closedLoop: boolean = false
    ): Vec3[] {
        // 处理特殊情况：控制点不足
        const pointCount = controlPoints.length;
        if (pointCount === 0) return [];
        if (pointCount === 1) return [controlPoints[0].clone()];
        
        // 处理2个控制点的情况（自动转为线性插值）
        if (pointCount === 2) {
            return this.generateLinearCurve(controlPoints, segmentsPerSegment, closedLoop);
        }
        
        // 处理3个控制点的情况（添加虚拟点）
        if (pointCount === 3) {
            return this.handleThreePoints(controlPoints, segmentsPerSegment, closedLoop);
        }
        
        // 正常处理4个及以上控制点
        const curvePoints: Vec3[] = [];
        const count = closedLoop ? pointCount : pointCount - 3;

        for (let i = 0; i < count; i++) {
            const p0 = controlPoints[(i - 1 + pointCount) % pointCount];
            const p1 = controlPoints[i % pointCount];
            const p2 = controlPoints[(i + 1) % pointCount];
            const p3 = controlPoints[(i + 2) % pointCount];

            // 端点处理
            const useP0 = closedLoop || i > 0;
            const useP3 = closedLoop || i < count - 1;

            for (let j = 0; j <= segmentsPerSegment; j++) {
                const t = j / segmentsPerSegment;
                const point = this.calculatePoint(
                    useP0 ? p0 : p1,
                    p1,
                    p2,
                    useP3 ? p3 : p2,
                    t
                );
                curvePoints.push(point);
            }
        }

        return curvePoints;
    }

    /**
     * 处理3个控制点的情况
     * @param points 控制点数组（长度必须为3）
     */
    private static handleThreePoints(
        points: Vec3[],
        segments: number,
        closedLoop: boolean
    ): Vec3[] {
        if (closedLoop) {
            // 闭合曲线：直接使用三个点形成闭环
            return this.generateCurveForThreePointsClosed(points, segments);
        }
        
        // 开曲线：添加虚拟点来创建平滑曲线
        return this.generateCurveForThreePointsOpen(points, segments);
    }

    /**
     * 为三个点生成闭合曲线
     */
    private static generateCurveForThreePointsClosed(
        points: Vec3[],
        segments: number
    ): Vec3[] {
        const [A, B, C] = points;
        const curvePoints: Vec3[] = [];
        
        // 第一段：C -> A -> B -> C
        for (let i = 0; i <= segments; i++) {
            const t = i / segments;
            const point = this.calculatePoint(C, A, B, C, t);
            curvePoints.push(point);
        }
        
        // 第二段：A -> B -> C -> A
        for (let i = 0; i <= segments; i++) {
            const t = i / segments;
            const point = this.calculatePoint(A, B, C, A, t);
            curvePoints.push(point);
        }
        
        // 第三段：B -> C -> A -> B
        for (let i = 0; i <= segments; i++) {
            const t = i / segments;
            const point = this.calculatePoint(B, C, A, B, t);
            curvePoints.push(point);
        }
        
        return curvePoints;
    }

    /**
     * 为三个点生成开曲线（添加虚拟点）
     */
    private static generateCurveForThreePointsOpen(
        points: Vec3[],
        segments: number
    ): Vec3[] {
        const [A, B, C] = points;
        const curvePoints: Vec3[] = [];
        
        // 创建虚拟点：在A前添加镜像点
        const virtualA = v3(2 * A.x - B.x, 2 * A.y - B.y, 2 * A.z - B.z);
        
        // 创建虚拟点：在C后添加镜像点
        const virtualC = v3(2 * C.x - B.x, 2 * C.y - B.y, 2 * C.z - B.z);
        
        // 第一段：virtualA -> A -> B -> C
        for (let i = 0; i <= segments; i++) {
            const t = i / segments;
            const point = this.calculatePoint(virtualA, A, B, C, t);
            curvePoints.push(point);
        }
        
        // 第二段：A -> B -> C -> virtualC
        for (let i = 0; i <= segments; i++) {
            const t = i / segments;
            const point = this.calculatePoint(A, B, C, virtualC, t);
            curvePoints.push(point);
        }
        
        return curvePoints;
    }

    /**
     * 生成线性插值曲线（用于2个控制点）
     */
    private static generateLinearCurve(
        points: Vec3[],
        segments: number,
        closedLoop: boolean
    ): Vec3[] {
        const [A, B] = points;
        const curvePoints: Vec3[] = [];
        
        if (closedLoop) {
            // A -> B
            for (let i = 0; i <= segments; i++) {
                const t = i / segments;
                curvePoints.push(Vec3.lerp(v3(), A, B, t));
            }
            
            // B -> A
            for (let i = 0; i <= segments; i++) {
                const t = i / segments;
                curvePoints.push(Vec3.lerp(v3(), B, A, t));
            }
        } else {
            // 简单线性插值
            for (let i = 0; i <= segments; i++) {
                const t = i / segments;
                curvePoints.push(Vec3.lerp(v3(), A, B, t));
            }
        }
        
        return curvePoints;
    }

    /**
     * Catmull-Rom单点插值计算
     * @param p0 前一点
     * @param p1 起点
     * @param p2 终点
     * @param p3 后一点
     * @param t 插值比例 (0~1)
     * @returns 插值点坐标
     */
    public static calculatePoint(p0: Vec3, p1: Vec3, p2: Vec3, p3: Vec3, t: number): Vec3 {
        const t2 = t * t;
        const t3 = t2 * t;

        // Catmull-Rom基函数系数
        const c0 = -0.5 * t3 + t2 - 0.5 * t;
        const c1 = 1.5 * t3 - 2.5 * t2 + 1;
        const c2 = -1.5 * t3 + 2 * t2 + 0.5 * t;
        const c3 = 0.5 * t3 - 0.5 * t2;

        // 组合各控制点影响
        const x = c0 * p0.x + c1 * p1.x + c2 * p2.x + c3 * p3.x;
        const y = c0 * p0.y + c1 * p1.y + c2 * p2.y + c3 * p3.y;
        const z = c0 * p0.z + c1 * p1.z + c2 * p2.z + c3 * p3.z;

        return v3(x, y, z);
    }

    /**
     * 在曲线上根据进度获取点
     * @param curvePoints 曲线点数组
     * @param progress 进度 (0~1)
     * @returns 对应的曲线点
     */
    public static getPointAtProgress(curvePoints: Vec3[], progress: number): Vec3 {
        if (curvePoints.length === 0) return v3();
        if (progress <= 0) return curvePoints[0].clone();
        if (progress >= 1) return curvePoints[curvePoints.length - 1].clone();
        
        const totalLength = this.estimateCurveLength(curvePoints);
        const targetLength = totalLength * progress;
        
        let accumulatedLength = 0;
        for (let i = 1; i < curvePoints.length; i++) {
            const segmentLength = Vec3.distance(curvePoints[i-1], curvePoints[i]);
            
            if (accumulatedLength + segmentLength >= targetLength) {
                const ratio = (targetLength - accumulatedLength) / segmentLength;
                return Vec3.lerp(v3(), curvePoints[i-1], curvePoints[i], ratio);
            }
            
            accumulatedLength += segmentLength;
        }
        
        return curvePoints[curvePoints.length - 1].clone();
    }

    /**
     * 估算曲线长度
     * @param curvePoints 曲线点数组
     * @returns 曲线总长度
     */
    public static estimateCurveLength(curvePoints: Vec3[]): number {
        if (curvePoints.length < 2) return 0;
        
        let length = 0;
        for (let i = 1; i < curvePoints.length; i++) {
            length += Vec3.distance(curvePoints[i-1], curvePoints[i]);
        }
        return length;
    }
}